/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sentry.provider.db.audit;

import java.util.Collection;
import org.apache.hadoop.conf.Configuration;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsRequest;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsResponse;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddUsersRequest;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddUsersResponse;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteGroupsResponse;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteUsersRequest;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteUsersResponse;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeResponse;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
import org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeResponse;
import org.apache.sentry.api.service.thrift.TCreateSentryRoleRequest;
import org.apache.sentry.api.service.thrift.TCreateSentryRoleResponse;
import org.apache.sentry.api.service.thrift.TDropSentryRoleRequest;
import org.apache.sentry.api.service.thrift.TDropSentryRoleResponse;
import org.apache.sentry.api.service.thrift.TSentryAuthorizable;
import org.apache.sentry.api.service.thrift.TSentryPrincipalType;
import org.apache.sentry.provider.db.log.entity.JsonLogEntity;
import org.apache.sentry.provider.db.log.entity.JsonLogEntityFactory;
import org.apache.sentry.provider.db.log.util.Constants;
import org.apache.sentry.service.thrift.TSentryResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class has methods to help log Sentry actions or events that need to be printed in a proper
 * format for Auditing log tools.
 */
public class SentryAuditLogger {
  private static final Logger ERROR_LOGGER = LoggerFactory.getLogger(SentryAuditLogger.class);
  private static final Logger AUDIT_LOGGER = LoggerFactory.getLogger(Constants.AUDIT_LOGGER_NAME);
  private static final JsonLogEntityFactory JSON_LOG_ENTITY = JsonLogEntityFactory.getInstance();

  /**
   * Configuration values required to create all JSON log entities.
   */
  private final Configuration conf;

  /**
   * Constructs a {@link SentryAuditLogger} with the desired configuration passed as a parameter.
   *
   * @param conf The configuration values necessary to create JSON log entities.
   */
  public SentryAuditLogger(Configuration conf) {
    this.conf = conf;
  }

  /**
   * Creates an audit log for the create role event.
   *
   * @param request The create role request received by the Sentry server.
   * @param response The create role response generated by the Sentry server.
   */
  public void onCreateRole(TCreateSentryRoleRequest request, TCreateSentryRoleResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for creating a role: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the drop role event.
   *
   * @param request The drop role request received by the Sentry server.
   * @param response The drop role response generated by the Sentry server.
   */
  public void onDropRole(TDropSentryRoleRequest request, TDropSentryRoleResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for creating a role: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the grant role privilege event.
   *
   * @param request The grant role privilege request received by the Sentry server.
   * @param response The grant role privilege response generated by the Sentry server.
   */
  public void onGrantRolePrivilege(TAlterSentryRoleGrantPrivilegeRequest request, TAlterSentryRoleGrantPrivilegeResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntities(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for grant role privilege: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the grant owner privilege event.
   *
   * @param status The response status of Sentry when granting the privilege.
   * @param requestorUserName The user name on behalf this grant is made
   * @param ownerType The principal type who to grant the owner privilege.
   * @param ownerName The name of the owner who to grant the owner privilege.
   * @param authorizable The authorizable object where to grant the owner privilege.
   */
  public void onGrantOwnerPrivilege(TSentryResponseStatus status, String requestorUserName,
    TSentryPrincipalType ownerType, String ownerName, TSentryAuthorizable authorizable) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(Constants.OPERATION_GRANT_OWNER_PRIVILEGE,
        Constants.OBJECT_TYPE_PRINCIPAL, requestorUserName, status, authorizable,
        ownerType, ownerName, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for grant owner privilege: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the transfer owner privilege event.
   *
   * @param status The response status of Sentry when transferring the privilege.
   * @param requestorUserName The user name on behalf this transfer is made
   * @param ownerType The principal type who to transfer the owner privilege.
   * @param ownerName The name of the owner who to transfer the owner privilege.
   * @param authorizable The authorizable object where to transfer the owner privilege.
   */
  public void onTransferOwnerPrivilege(TSentryResponseStatus status, String requestorUserName,
    TSentryPrincipalType ownerType, String ownerName, TSentryAuthorizable authorizable) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(Constants.OPERATION_TRANSFER_OWNER_PRIVILEGE,
        Constants.OBJECT_TYPE_PRINCIPAL, requestorUserName, status, authorizable,
        ownerType, ownerName, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for grant owner privilege: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the revoke role privilege event.
   *
   * @param request The revoke role privilege request received by the Sentry server.
   * @param response The revoke role privilege response generated by the Sentry server.
   */
  public void onRevokeRolePrivilege(TAlterSentryRoleRevokePrivilegeRequest request, TAlterSentryRoleRevokePrivilegeResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntities(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for revoke role privilege: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the revoke user owner privilege event.
   *
   * @param requestorUserName The user name on behalf this revoke is made
   * @param status The response status of Sentry when revoking the privilege.
   * @param userName The name of the user who to revoke the owner privilege.
   * @param authorizable The authorizable object where to revoke the owner privilege.
   */
  public void onRevokeUserOwnerPrivilege(String requestorUserName, TSentryResponseStatus status,
    String userName, TSentryAuthorizable authorizable) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(Constants.OPERATION_REVOKE_PRIVILEGE,
        Constants.OBJECT_TYPE_PRINCIPAL, requestorUserName, status, authorizable,
        TSentryPrincipalType.USER, userName, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for grant user owner privilege: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the role add groups event.
   *
   * @param request The role add groups request received by the Sentry server.
   * @param response The role add groups response generated by the Sentry server.
   */
  public void onGrantRoleToGroup(TAlterSentryRoleAddGroupsRequest request, TAlterSentryRoleAddGroupsResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for adding groups to a role: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the role delete groups event.
   *
   * @param request The role delete groups request received by the Sentry server.
   * @param response The role delete groups response generated by the Sentry server.
   */
  public void onRevokeRoleFromGroup(TAlterSentryRoleDeleteGroupsRequest request, TAlterSentryRoleDeleteGroupsResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for deleting groups from a role: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the role add users event.
   *
   * @param request The role add users request received by the Sentry server.
   * @param response The role add users response generated by the Sentry server.
   */
  public void onGrantRoleToUser(TAlterSentryRoleAddUsersRequest request, TAlterSentryRoleAddUsersResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for adding users to a role: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  /**
   * Creates an audit log for the role delete users event.
   *
   * @param request The role delete users request received by the Sentry server.
   * @param response The role delete users response generated by the Sentry server.
   */
  public void onRevokeRoleFromUser(TAlterSentryRoleDeleteUsersRequest request, TAlterSentryRoleDeleteUsersResponse response) {
    try {
      info(JSON_LOG_ENTITY.createJsonLogEntity(request, response, conf));
    } catch (Exception e) {
      String msg = "Cannot generate an audit log for deleting users from a role: " + e.getMessage();
      ERROR_LOGGER.error(msg, e);
    }
  }

  private void info(Collection<JsonLogEntity> jsonLogEntities) throws Exception {
    for (JsonLogEntity jsonLogEntity : jsonLogEntities) {
      info(jsonLogEntity);
    }
  }

  private void info(JsonLogEntity jsonLogEntity) throws Exception {
    AUDIT_LOGGER.info(jsonLogEntity.toJsonFormatLog());
  }
}
